﻿using Pangeo.Admin.Interface.HrmDb;
using Pangeo.Admin.Models.HrmDb.Dto;
using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
using System.Collections.Concurrent;
using System.Drawing;
using Pangeo.Admin.Models;
using Pangeo.Admin.Models.BaBizDb.Entity;
using Amazon.Runtime.Internal.Transform;

namespace Pangeo.Admin.Services.HrmDb
{
    public class HrmService: _ServerBase, IHrm
    {
        public HrmService(IConfiguration config) : base(config) { }

        #region 人员出勤情况

        public Task<WorkResult> CalcWorkResult(DateTime calcStartTime, DateTime calcEndTime)
        {
            var workResult = new WorkResult();

            // Step 1 获取加班申请数据
            var tmAbsenceOfThreesTask = Task.Run(() =>
            {
                var tmAbsenceOfThrees = GetTmAbsenceOfThrees(calcStartTime, calcEndTime);

                if (tmAbsenceOfThrees.Success)
                {
                    try
                    {
                        tmAbsenceOfThrees.Data = tmAbsenceOfThrees.Data.OrderBy(t => t.create_start_date).OrderBy(t => t.people_no).ToList();

                        var bags = new ConcurrentBag<TmAbsenceOfThreeItem>();
                        Parallel.ForEach(tmAbsenceOfThrees.Data, t =>
                        {
                            //if (t._id.ToString() == "667cb594f2fa0ca7ad034c2d")
                            //    t.create_start_date = t.create_start_date;

                            t.create_start_date = t.create_start_date.AddHours(8);
                            t.create_end_date = t.create_end_date.AddHours(8);

                            for (var i = 0; i < t.effective_date.Length; i++)
                            {
                                if (t.effective_date[i].Length == 0)
                                    continue;

                                var startTime = t.effective_date[i].FirstOrDefault()?.start_date.Value;
                                var endTime = t.effective_date[i].FirstOrDefault()?.end_date.Value;

                                // || startTime > calcEndTime || endTime < calcStartTime 先不要加这个条件，没有问题后再尝试加
                                if (startTime == null || endTime == null)
                                    continue;

                                var item = new TmAbsenceOfThreeItem();

                                item.work_on_time = startTime.Value.AddHours(8);
                                item.work_off_time = endTime.Value.AddHours(8);

                                if (t.total_time.Length > 0 && i < t.total_time.Length)
                                    item.total_time = t.total_time[i];

                                if (t.conclusion.Length > 0 && i < t.conclusion.Length)
                                    item.conclusion = t.conclusion[i];

                                item._id = t._id;
                                item.people_no = t.people_no;
                                item.people_name = t.people_name;

                                bags.Add(item);
                            }
                        });

                        workResult.Absence = bags.OrderBy(t => t.work_on_time).OrderBy(t => t.people_no).ToList();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                        Console.ReadLine();
                    }
                }
                else
                {
                    Console.WriteLine(tmAbsenceOfThrees.Message);
                }
            });

            // Step 2 获取打卡明细数据
            var tmClockRecordTempsTask = Task.Run(() =>
            {
                var tmClockRecordTemps = GetTmClockRecordTemps(calcStartTime, calcEndTime);

                if (tmClockRecordTemps.Success)
                {
                    tmClockRecordTemps.Data = tmClockRecordTemps.Data.OrderBy(t => t.clock_time).OrderBy(t => t.people_no).ToList();

                    var bags = new ConcurrentBag<TmClockRecordTemps>();
                    Parallel.ForEach(tmClockRecordTemps.Data, t =>
                    {
                        // 注意：打卡时间是ISO时间，比如早上08:30打卡，数据库存储的是 2024-06-04T00:30:19.000Z
                        t.clock_time = DateTime.Parse(t.clock_time.AddHours(8).ToString("yyyy-MM-dd HH:mm:ss"));

                        if (!t.card_source.StartsWith("for_leave"))
                            bags.Add(t);
                    });

                    // 过滤请假的数据，只需要打卡、补卡的数据
                    workResult.Clock = bags.ToList();
                }
                else
                {
                    Console.WriteLine(tmClockRecordTemps.Message);
                }
            });

            // Step 3 获取请假申请数据
            var tmLeaveOfAbsencesTime = new List<TmLeaveOfAbsencesTime>();
            var tmLeaveOfAbsencesTask = Task.Run(() =>
            {
                var tmLeaveOfAbsences = GetTmLeaveOfAbsences(calcStartTime, calcEndTime);

                if (tmLeaveOfAbsences.Success)
                {
                    tmLeaveOfAbsences.Data = tmLeaveOfAbsences.Data.OrderBy(t => t.create_start_date).OrderBy(t => t.people_no).ToList();

                    var bags = new ConcurrentBag<TmLeaveOfAbsencesTime>();
                    Parallel.ForEach(tmLeaveOfAbsences.Data, t =>
                    {
                        //if (t._id.ToString() == "66626fc9c15d1923058cb6a8")
                        //    t.create_start_date = t.create_start_date;

                        t.create_start_date = t.create_start_date.AddHours(8);
                        t.create_end_date = t.create_end_date.AddHours(8);

                        if (HoursDiff(t.create_start_date, t.create_end_date, t.hours + 0.5))
                        {
                            var leaveTime = new TmLeaveOfAbsencesTime();
                            leaveTime._id = t._id;
                            leaveTime.people_no = t.people_no;
                            leaveTime.people_name = t.people_name;
                            leaveTime.start_time = t.create_start_date;
                            leaveTime.end_time = t.create_end_date;
                            leaveTime.hours = t.hours;

                            if (leaveTime.end_time < leaveTime.start_time) // 修复HRM请假结束时间错误的问题
                                leaveTime.end_time = leaveTime.end_time.AddDays(1);

                            bags.Add(leaveTime);
                            return;
                        }

                        for (var i = 0; i < t.date_arr.Length; i++)
                        {
                            // 确保索引对应
                            if (i + 1 > t.start_time_arr.Length)
                                break;
                            if (i + 1 > t.end_time_arr.Length)
                                break;
                            if (i + 1 > t.leave_hours_arr.Length)
                                break;

                            var leaveTime = new TmLeaveOfAbsencesTime();
                            leaveTime._id = t._id;
                            leaveTime.people_no = t.people_no;
                            leaveTime.people_name = t.people_name;

                            if (t.date_arr[i] == "24:00")
                                leaveTime.start_time = DateTime.Parse($"{t.date_arr[i]} 00:00").AddDays(1);
                            else
                                leaveTime.start_time = DateTime.Parse($"{t.date_arr[i]} {t.start_time_arr[i]}");

                            if (t.end_time_arr[i] == "24:00")
                                leaveTime.end_time = DateTime.Parse($"{t.date_arr[i]} 00:00").AddDays(1);
                            else
                                leaveTime.end_time = DateTime.Parse($"{t.date_arr[i]} {t.end_time_arr[i]}");

                            leaveTime.hours = t.leave_hours_arr[i];

                            if (leaveTime.end_time < leaveTime.start_time) // 修复HRM请假结束时间错误的问题
                                leaveTime.end_time = leaveTime.end_time.AddDays(1);

                            bags.Add(leaveTime);
                        }
                    });

                    workResult.Leave = bags.ToList();
                }
                else
                {
                    Console.WriteLine(tmLeaveOfAbsences.Message);
                }
            });

            // 等待前面的任务完成
            Task.WaitAll(tmAbsenceOfThreesTask, tmClockRecordTempsTask, tmLeaveOfAbsencesTask);

            // Step 4 获取排班数据，并关联打卡、加班申请数据
            var tmWorkPlanPeoples = GetTmWorkPlanPeoples(calcStartTime, calcEndTime);
            var overTimeRltList = new List<OverTimeRlt>();
            if (tmWorkPlanPeoples.Success)
            {
                tmWorkPlanPeoples.Data = tmWorkPlanPeoples.Data.OrderBy(t => t.date).OrderBy(t => t.people_no).ToList();

                OverTimeRlt overTime;
                List<TmClockRecordTemps> clocks;
                DateTime clock_filter_begin, clock_filter_end, filterStart, filterEnd;
                DateTime? workStartTime = null, workEndTime = null;
                List<TimeRange> ignoreTimeRanges;
                TmWorkPlanPeoples workPlan;
                List<TmAbsenceOfThreeItem> itemsToRemove;
                DateTime dateTime;
                TimeSpan workTimeSpan, tmpTimeSpan;

                // 先计算第一条的排班情况
                CalcWorkPlanTime(tmWorkPlanPeoples.Data[0]);
                for (int i = 0; i < tmWorkPlanPeoples.Data.Count; i++)
                {
                    workPlan = tmWorkPlanPeoples.Data[i];

                    // 计算次日的排班信息（计算下班卡需要用到）
                    if (i + 1 < tmWorkPlanPeoples.Data.Count)
                        CalcWorkPlanTime(tmWorkPlanPeoples.Data[i + 1]);

                    // 计算打卡时间
                    clock_filter_begin = workPlan.start.AddHours(-2.5); // 最多提前2.5个小时打卡

                    if (i + 1 < tmWorkPlanPeoples.Data.Count && workPlan.people_no == tmWorkPlanPeoples.Data[i + 1].people_no) // 下班卡取次日班次开始时间之前所有打卡记录里最晚的那条
                    {
                        clock_filter_end = tmWorkPlanPeoples.Data[i + 1].start.AddHours(-2);
                    }
                    else // 临界值
                    {
                        if (workPlan.is_cross_day)
                            clock_filter_end = DateTime.Parse($"{workPlan.date.AddDays(1).ToString("yyyy-MM-dd")} 13:00:00");
                        else
                            clock_filter_end = DateTime.Parse($"{workPlan.date.ToString("yyyy-MM-dd")} 23:59:59");
                    }

                    // 关联打卡明细数据
                    clocks = workResult.Clock.Where(c => c.people_no == workPlan.people_no).ToList();

                    clocks = clocks.Where(c => c.clock_time >= clock_filter_begin && c.clock_time <= clock_filter_end).ToList();

                    if (clocks.Count > 1)
                    {
                        clocks = clocks.OrderBy(c => c.clock_time).ToList();
                        workPlan.first_clock = clocks.First().clock_time;

                        if (clocks.Count > 2) // 至少有3个
                        {
                            for (var j = clocks.Count - 1; j > 0; j--)
                            {
                                // 打卡时间与下班时间相差了16个小时以内，为下班卡
                                if (HoursDiff(workPlan.first_clock.Value, clocks[j].clock_time, 16))
                                {
                                    workPlan.last_clock = clocks[j].clock_time;
                                    break;
                                }
                            }
                        }

                        if (workPlan.last_clock == null)
                            workPlan.last_clock = clocks.Last().clock_time;

                        clocks.ForEach(clock => workResult.Clock.Remove(clock)); // 后面不再使用到，使用完后删除，提高下次查询速度
                    }
                    else if (clocks.Count == 1)
                    {
                        workPlan.first_clock = clocks.First().clock_time;
                    }

                    // 关联请假时间
                    Parallel.ForEach(tmLeaveOfAbsencesTime, l =>
                    {
                        if (l.people_no == workPlan.people_no && l.start_time >= workPlan.start && l.end_time <= workPlan.end)
                            workPlan.leave_hours += l.hours;
                    });

                    // 关联加班申请明细
                    if (workPlan.name != null && workPlan.name.StartsWith("食堂")) // 食堂班次特殊计算
                    {
                        ignoreTimeRanges = CalcIgnoreTime(workPlan, calcStartTime, calcEndTime, false); // 计算休息时间段
                        foreach (var item in workResult.Absence)
                        {
                            if (item.is_used || item.people_no != workPlan.people_no || workPlan.work_on_time < calcStartTime || workPlan.work_on_time > calcEndTime) //此处不能排除 item.total_time == 0，比如HRM不认可休息时间段加班，故加班时间为0
                                continue;

                            if (workPlan.work_off_time.HasValue && HoursDiff(workPlan.work_off_time.Value, item.work_on_time)) // 连续加班
                            {
                                if (!item.is_calc)
                                {
                                    item.total_time = CalcEffectiveWorkOverHours(item.work_on_time, item.work_off_time, ignoreTimeRanges);
                                    item.is_calc = true;
                                }

                                workPlan.work_off_time = item.work_off_time;
                                workPlan.total_hours += item.total_time;
                                item.is_used = true;

                                continue;
                            }

                            if (workPlan.is_cross_day) // 跨天
                            {
                                if (HoursDiff(item.work_on_time, workPlan.end) || // 连续加班
                                    HoursDiff(item.work_on_time, workPlan.start)) // 无排班加班
                                {
                                    if (!item.is_calc)
                                    {
                                        item.total_time = CalcEffectiveWorkOverHours(item.work_on_time, item.work_off_time, ignoreTimeRanges);
                                        item.is_calc = true;
                                    }

                                    workPlan.work_on_time = item.work_on_time;
                                    workPlan.conclusion = item.conclusion;
                                    workPlan.total_hours = item.total_time;
                                    workPlan.work_off_time = item.work_off_time;
                                    item.is_used = true;

                                    continue;
                                }
                                // 可能是排班之外的加班（晚班申请加白班、白班申请加晚班） eg: H007453 罗双燕 2024-05-11 晚班申请加白班
                                else if (item.work_on_time >= calcStartTime && item.work_on_time <= calcEndTime && IsSameDay(item.work_on_time, workPlan.date))
                                {
                                    if (!item.is_calc)
                                    {
                                        item.total_time = CalcEffectiveWorkOverHours(item.work_on_time, item.work_off_time, ignoreTimeRanges);
                                        item.is_calc = true;
                                    }

                                    if (workPlan.work_on_time == null)
                                    {
                                        workPlan.work_on_time = item.work_on_time;
                                        workPlan.conclusion = item.conclusion;
                                        workPlan.total_hours = item.total_time;
                                        workPlan.work_off_time = item.work_off_time;
                                    }
                                    else
                                    {
                                        workPlan.work_off_time = item.work_off_time;
                                        workPlan.total_hours += item.total_time;
                                    }

                                    item.is_used = true;

                                    continue;
                                }
                            }
                            else if (IsSameDay(item.work_on_time, workPlan.date))
                            {
                                if (!item.is_calc)
                                {
                                    item.total_time = CalcEffectiveWorkOverHours(item.work_on_time, item.work_off_time, ignoreTimeRanges);
                                    item.is_calc = true;
                                }

                                workPlan.work_on_time = item.work_on_time;
                                workPlan.conclusion = item.conclusion;
                                workPlan.total_hours = item.total_time;
                                workPlan.work_off_time = item.work_off_time;
                                item.is_used = true;

                                continue;
                            }
                        }

                        // 没有排班，班次时间需要计算进去（根据打卡时间计算）
                        if (!workPlan.is_job_day && workPlan.total_hours < workPlan.work_hours && workPlan.first_clock.HasValue && workPlan.last_clock.HasValue)
                        {
                            // 情况一：打卡时间符合班次时间，但是加班时间在班次外，需要加上班次时间，
                            // 比如：没有排班上班08:00~17:30，加班明细填07:30~08:00或17:30~18:00，实际8.5H
                            if (workPlan.first_clock <= workPlan.start.AddMinutes(10) && workPlan.last_clock.Value.AddMinutes(10) >= workPlan.end) // 允许迟到10分钟
                            {
                                workPlan.total_hours += workPlan.work_hours;
                            }
                            else //
                            {

                            }
                        }
                    }
                    else // 非食堂班次加班时间直接从HRM取
                    {
                        // 有加班时间，则不管是否工作日，计算正常班次的夜班时长
                        if (workPlan.is_job_day && workPlan.first_clock.HasValue && workPlan.last_clock.HasValue)
                        {
                            // 迟到、早退以实际打卡时间为准
                            workStartTime = workPlan.first_clock > workPlan.start ? workPlan.first_clock : workPlan.start;
                            workEndTime = workPlan.last_clock < workPlan.end ? workPlan.last_clock : workPlan.end;

                            workPlan.night_hours = CalcNightTime(workPlan, calcStartTime, calcEndTime, workStartTime.Value, workEndTime.Value);

                            //if(workEndTime.Value > calcEndTime)
                            // 退出加班时间循环
                        }

                        filterStart = workPlan.date.AddDays(-1);
                        filterEnd = workPlan.date.AddDays(2);

                        itemsToRemove = new List<TmAbsenceOfThreeItem>();

                        foreach (var item in workResult.Absence)
                        {
                            // 清除已使用的数据，减少下次的循环时间
                            if (item.is_used)
                            {
                                itemsToRemove.Add(item);
                                continue;
                            }

                            // 注意该条件必须放在前面，否则会导致加班时间不准确（比如：用张三的加班时间比较李四的排班时间，判断之前必须先确保是同一个人）
                            if (item.people_no != workPlan.people_no) // 为方便调试，与下面的条件拆开
                                continue;

                            //if (item._id.ToString() == "6655240d2386de226ba07bae" && item.work_on_time.ToString("yyMMdd") == "240530")
                            //    item.is_used = item.is_used;

                            // 兼容：加班时间填错的问题（工作日仍然填写加班单据的问题，删除该数据）
                            if (workPlan.is_job_day && item.work_on_time >= workPlan.start && item.work_off_time <= workPlan.end)
                            {
                                itemsToRemove.Add(item);
                                continue;
                            }

                            // 排除不在范围内的加班，加上calcEndTime是为了排除临界值之后的加班时间被计算进去
                            if (item.work_on_time > filterEnd || item.work_off_time < filterStart || item.work_on_time > calcEndTime)
                                continue;

                            // 修正加班开始、结束时间（有的加班明细开始时间是19:48）
                            if (item.work_on_time != workPlan.start && HoursDiff(item.work_on_time, workPlan.start, 0.4))
                                item.work_on_time = workPlan.start;

                            // 兼容：填错加班时间的情况
                            if (item.work_on_time > item.work_off_time)
                            {
                                dateTime = item.work_on_time;
                                item.work_on_time = item.work_off_time;
                                item.work_off_time = dateTime;
                            }

                            if (workPlan.work_off_time.HasValue && HoursDiff(workPlan.work_off_time.Value, item.work_on_time)) // 连续加班
                            {
                                workPlan.work_off_time = item.work_off_time;
                                workPlan.total_hours += item.total_time;
                                workPlan.night_hours += CalcNightTime(workPlan, calcStartTime, calcEndTime, item.work_on_time, item.work_off_time);
                                item.is_used = true;

                                continue;
                            }

                            if (workPlan.is_cross_day) // 跨天
                            {
                                if (HoursDiff(item.work_on_time, workPlan.end) || // 连续加班
                                    HoursDiff(item.work_on_time, workPlan.start)) // 无排班加班
                                {
                                    workPlan.work_on_time = item.work_on_time;
                                    workPlan.conclusion = item.conclusion;
                                    workPlan.total_hours = item.total_time;
                                    workPlan.night_hours += CalcNightTime(workPlan, calcStartTime, calcEndTime, item.work_on_time, item.work_off_time);
                                    workPlan.work_off_time = item.work_off_time;
                                    item.is_used = true;

                                    continue;
                                }
                                // 可能是排班之外的加班（晚班申请加白班、白班申请加晚班） eg: H007453 罗双燕 2024-05-11 晚班申请加白班
                                else if (item.work_on_time >= calcStartTime && item.work_on_time <= calcEndTime &&
                                    IsSameDay(item.work_on_time, workPlan.date) &&
                                    item.work_on_time > workPlan.date.AddHours(2)) // 排除 06.21 00:00 ~ 08:00
                                {
                                    if (workPlan.work_on_time == null)
                                    {
                                        workPlan.work_on_time = item.work_on_time;
                                        workPlan.conclusion = item.conclusion;
                                        workPlan.total_hours = item.total_time;
                                        workPlan.night_hours += CalcNightTime(workPlan, calcStartTime, calcEndTime, item.work_on_time, item.work_off_time);
                                        workPlan.work_off_time = item.work_off_time;
                                    }
                                    else
                                    {
                                        workPlan.work_off_time = item.work_off_time;
                                        workPlan.total_hours += item.total_time;
                                    }

                                    item.is_used = true;

                                    continue;
                                }
                            }
                            else if (IsSameDay(item.work_on_time, workPlan.date))
                            {
                                workPlan.work_on_time = item.work_on_time;
                                workPlan.conclusion = item.conclusion;
                                workPlan.total_hours = item.total_time;
                                workPlan.night_hours += CalcNightTime(workPlan, calcStartTime, calcEndTime, item.work_on_time, item.work_off_time);
                                workPlan.work_off_time = item.work_off_time;
                                item.is_used = true;

                                continue;
                            }
                        }

                        // 从原列表中移除已处理的项
                        foreach (var item in itemsToRemove)
                            workResult.Absence.Remove(item);
                    }

                    // 加班结论
                    if (workPlan.night_hours > 0)
                    {
                        overTime = overTimeRltList.SingleOrDefault(o => o.people_no == workPlan.people_no);
                        if (overTime == null)
                        {
                            overTime = new OverTimeRlt();
                            overTimeRltList.Add(overTime);
                        }

                        overTime.people_no = workPlan.people_no;

                        if (workPlan.is_job_day)
                            overTime.job_day += workPlan.night_hours;
                        else if (workPlan.is_holiday_day)
                            overTime.holiday_day += workPlan.night_hours;
                        else
                            overTime.off_day += workPlan.night_hours;

                        overTime.total += workPlan.night_hours;
                        overTime.people_name = workPlan.people_name;
                    }

                    // 班次异常
                    if (!workPlan.is_cross_day && workPlan.work_on_time.HasValue && workPlan.work_off_time.HasValue &&
                        !IsSameDay(workPlan.work_on_time.Value, workPlan.work_off_time.Value))
                    {
                        workTimeSpan = new TimeSpan(workPlan.work_off_time.Value.Hour, workPlan.work_off_time.Value.Minute, workPlan.work_off_time.Value.Second);
                        tmpTimeSpan = new TimeSpan(1, 0, 0);

                        // 排除 01:00 之前的加班情况（白班连续加班）
                        if (workTimeSpan.CompareTo(tmpTimeSpan) > 0)
                        {
                            workPlan.remark = "班次异常";
                            workPlan.bgColor = Color.Yellow;
                        }
                    }

                    // 无排班，无加班申请，有打卡记录
                    if (!workPlan.is_job_day && !workPlan.work_on_time.HasValue && workPlan.first_clock.HasValue && workPlan.last_clock.HasValue)
                    {
                        workPlan.remark = "无排班、加班，有打卡";
                        workPlan.bgColor = Color.Green;
                    }

                    // 无排班，晚班加班时间异常
                    if (!workPlan.is_job_day && workPlan.name.Contains("晚班") && workPlan.work_off_time.HasValue && workPlan.total_hours > 0 && workPlan.total_hours < 11)
                    {
                        workPlan.remark = "无排班，晚班加班时间异常";
                        workPlan.bgColor = Color.OrangeRed;
                    }

                    // 正常排班，未报加班
                    if (workPlan.is_job_day && !workPlan.work_on_time.HasValue && workPlan.last_clock.HasValue && workPlan.end.AddHours(0.5) < workPlan.last_clock)
                    {
                        workTimeSpan = new TimeSpan(workPlan.last_clock.Value.Hour, workPlan.last_clock.Value.Minute, workPlan.last_clock.Value.Second);
                        tmpTimeSpan = new TimeSpan(20, 30, 0);

                        // 只选出夜班的时间
                        if (workTimeSpan.CompareTo(tmpTimeSpan) > 0)
                        {
                            workPlan.remark = "未报加班";
                            workPlan.bgColor = Color.Brown;
                        }
                    }

                    // 加班时间填写异常
                    if (workPlan.work_on_time.HasValue && HoursDiff(workPlan.work_on_time.Value, workPlan.work_off_time.Value, 0.4))
                    {
                        workPlan.remark = "加班时间填写异常";
                        workPlan.bgColor = Color.OrangeRed;
                    }
                };

                workResult.WorkPeople = tmWorkPlanPeoples.Data;
            }

            return Task.FromResult(workResult);
        }

        /// <summary>
        /// 查询排班
        /// </summary>
        private Result<List<TmWorkPlanPeoples>> GetTmWorkPlanPeoples(DateTime startTime, DateTime endTime)
        {
            var param = new Dictionary<string, DateTime>()
            {
                ["@CalcStartTime"] = startTime.AddDays(-1),
                ["@CalcEndTime"] = endTime
            };

            // 有的是晚班连续加班，第二天早上才下班，但是第二天的下班时间可能属于计算加班时间范围内（计算开始时间），需要统计进来
            return Query<TmWorkPlanPeoples>(timeRange: param) ?? new() { Message = "获取[排班]数据失败！" };
        }

        /// <summary>
        /// 查询加班
        /// </summary>
        private Result<List<TmAbsenceOfThrees>> GetTmAbsenceOfThrees(DateTime startTime, DateTime endTime)
        {
            var param = new Dictionary<string, DateTime>()
            {
                ["@CalcStartTime"] = startTime.AddDays(-1),
                ["@CalcEndTime"] = endTime
            };

            // 有的是晚班连续加班，第二天早上才下班，但是第二天的下班时间可能属于计算加班时间范围内（计算开始时间），需要统计进来
            return Query<TmAbsenceOfThrees>(timeRange: param) ?? new() { Message = "获取[加班]数据失败！" };
        }

        /// <summary>
        /// 查询打卡记录
        /// </summary>
        private Result<List<TmClockRecordTemps>> GetTmClockRecordTemps(DateTime startTime, DateTime endTime)
        {
            var param = new Dictionary<string, DateTime>()
            {
                ["@CalcStartTime"] = startTime.AddDays(-1),
                ["@CalcEndTime"] = endTime.AddDays(1)
            };

            // 有的是晚班连续加班，第二天早上才下班，但是第二天的下班时间可能属于计算加班时间范围内（计算结束时间），需要统计进来
            return Query<TmClockRecordTemps>(timeRange: param) ?? new() { Message = "获取[打卡]数据失败！" };
        }

        /// <summary>
        /// 查询请假
        /// </summary>
        private Result<List<TmLeaveOfAbsences>> GetTmLeaveOfAbsences(DateTime startTime, DateTime endTime)
        {
            var param = new Dictionary<string, DateTime>()
            {
                ["@CalcStartTime"] = startTime.AddDays(-1),
                ["@CalcEndTime"] = endTime
            };

            // 有的是晚班连续加班，第二天早上才下班，但是第二天的下班时间可能属于计算加班时间范围内（计算开始时间），需要统计进来
            return Query<TmLeaveOfAbsences>(timeRange: param) ?? new() { Message = "获取[请假]数据失败！" };
        }

        private void CalcWorkPlanTime(TmWorkPlanPeoples workPlan)
        {
            workPlan.date = workPlan.date; // 排班日期不需要加8小时
            workPlan.start = DateTime.Parse($"{workPlan.date.ToString("yyyy-MM-dd")} {workPlan.start.AddHours(8).ToString("HH:mm")}");
            workPlan.break_hours = workPlan.break_mins / 60;

            if (workPlan.break_start.HasValue)
                workPlan.break_start = workPlan.break_start.Value.AddHours(8);
            if (workPlan.break_end.HasValue)
                workPlan.break_end = workPlan.break_end.Value.AddHours(8);
            if (workPlan.break_start2.HasValue)
                workPlan.break_start2 = workPlan.break_start2.Value.AddHours(8);
            if (workPlan.break_end2.HasValue)
                workPlan.break_end2 = workPlan.break_end2.Value.AddHours(8);

            if (workPlan.is_cross_day) // 跨天
                workPlan.end = DateTime.Parse($"{workPlan.date.AddDays(1).ToString("yyyy-MM-dd")} {workPlan.end.AddHours(8).ToString("HH:mm")}");
            else
                workPlan.end = DateTime.Parse($"{workPlan.date.ToString("yyyy-MM-dd")} {workPlan.end.AddHours(8).ToString("HH:mm")}");

            workPlan.work_hours = workPlan.work_mins / 60;
        }

        /// <summary>
        /// 计算夜班时长
        /// </summary>
        /// <returns></returns>
        private double CalcNightTime(TmWorkPlanPeoples workPlan, DateTime calcStartTime, DateTime calcEndTime, DateTime workOnTime, DateTime workOffTime)
        {
            if (!workPlan.last_clock.HasValue) // 没有下班卡的不计算
                return 0;

            // 加班开始、加班结束相差0.5H内的，判定加班时长为0
            if (HoursDiff(workOnTime, workOffTime, 0.49))
                return 0;

            if (!workPlan.first_clock.HasValue) // 没有上班卡的，补班次下班卡
                workPlan.first_clock = workPlan.end;

            var workOnDay = DateTime.Parse(workOnTime.ToString("yyyy-MM-dd"));
            var workOffDay = DateTime.Parse(workOffTime.ToString("yyyy-MM-dd"));

            if (workPlan.name.Contains("保安")) // 保安班次直接以打卡时间为准
            {
                if (workOffTime.ToString("HH:mm") != "00:00") // 分段加班，这种要分段计算
                    workOffTime = workPlan.last_clock.Value;
            }
            else
            {
                if (workPlan.first_clock.Value > workOnTime.AddMinutes(5))
                    workOnTime = workPlan.first_clock.Value;

                if (workPlan.last_clock.Value < workOffTime)
                    workOffTime = workPlan.last_clock.Value;
            }

            // 排除加班时间未跨天，且加班开始时间 >= 08:00、加班结束时间 < 当前20:00的
            var _workOnDay = DateTime.Parse(workOnTime.ToString("yyyy-MM-dd"));
            var _workOffDay = DateTime.Parse(workOffTime.ToString("yyyy-MM-dd"));
            if (IsSameDay(workOnTime, workOffTime) && workOnTime >= _workOnDay.AddHours(8) && workOffTime <= _workOffDay.AddHours(20))
                return 0;

            double total = 0;
            var ignoreTimeRanges = CalcIgnoreTime(workPlan, calcStartTime, calcEndTime, true);

            //if (work._id.ToString() == "6603d607d1e073716b8767b9")
            //    total = 0;

            //DateTime filterStart = workOnDay.AddHours(20), filterEnd = workOnDay.AddDays(1);
            ignoreTimeRanges.Add(new TimeRange(workOnDay.AddHours(8), workOnDay.AddHours(20))); // 忽略白班08::00~20:00
            if (!IsSameDay(workOnTime, workOffTime)) // 不是同一天
            {
                //filterEnd = workOffDay.AddHours(8);
                if (IsSameDay(workPlan.date, calcEndTime)) // 最后一天存在跨天上班的，排除第二天的所有时间
                    ignoreTimeRanges.Add(new TimeRange(workOffDay, workOffDay.AddHours(24)));
                else
                    ignoreTimeRanges.Add(new TimeRange(workOffDay.AddHours(8), workOffDay.AddHours(20)));
            }

            // 过滤请假的时间
            /*.ForEach(l =>
            {
                if (l.people_no == workPlan.people_no && l.start_time >= workPlan.start && l.end_time <= workPlan.end)
                    ignoreTimeRanges.Add(new TimeRange(l.start_time, l.end_time));
            });*/

            if (ignoreTimeRanges.Count > 0)
                ignoreTimeRanges = MergeOverlappingRanges(ignoreTimeRanges);

            total = CalcEffectiveWorkOverHours(workOnTime, workOffTime, ignoreTimeRanges);

            return total;
        }

        private DateTime FixWorkTime(DateTime workTime, bool isStart)
        {
            var timeStr = workTime.ToString("yyyy-MM-dd HH");
            DateTime beginTime, endTime, fixedTime = DateTime.Parse($"{timeStr}:00");

            if (isStart)
            {
                beginTime = DateTime.Parse($"{timeStr}:03");
                endTime = DateTime.Parse($"{timeStr}:33");

                if (workTime > endTime) // XX:34(含)之后打卡的，加1H
                    fixedTime = fixedTime.AddHours(1);
                else if (workTime > beginTime && workTime <= endTime) // XX:04(含)之后、XX:34之前打开，调整计算时间点为 XX:30
                    fixedTime = fixedTime.AddMinutes(30);
            }
            else
            {
                beginTime = DateTime.Parse($"{timeStr}:00");
                endTime = DateTime.Parse($"{timeStr}:30");

                if (workTime >= endTime)
                    fixedTime = endTime;
                else if (workTime >= beginTime && workTime < endTime)
                    fixedTime = beginTime;
            }

            return fixedTime;
        }

        private List<TimeRange> CalcIgnoreTime(TmWorkPlanPeoples t, DateTime calcStartTime, DateTime calcEndTime, bool isCalcNightTime)
        {
            TimeRange timerange;
            var list = new List<TimeRange>();
            TimeSpan restStartTimeSpan, restEndTimeSpan;
            var workTimeBeginSpan = new TimeSpan(t.start.Hour, t.start.Minute, 0);
            var workTimeEndSpan = new TimeSpan(23, 59, 59);
            var isExistRestTime = false;

            if (IsSameDay(t.date, calcStartTime, 1)) // 排班日期如果跟计算开始时间相差一天，则排除临界点时间
                list.Add(new TimeRange()
                {
                    Start = calcStartTime.AddDays(-1),
                    End = calcStartTime,
                });

            if (t.is_job_day) // 存在排班，需要剔除正常的班次的时间、休息的时间
            {
                if (isCalcNightTime) // 计算夜班时长，只排除休息时间
                {
                    if (t.break_start.HasValue && t.break_end.HasValue)
                    {
                        timerange = new TimeRange();
                        timerange.Start = DateTime.Parse($"{t.date.ToString("yyyy-MM-dd")} {t.break_start.Value.ToString("HH:mm")}");
                        timerange.End = DateTime.Parse($"{t.date.ToString("yyyy-MM-dd")} {t.break_end.Value.ToString("HH:mm")}");

                        restStartTimeSpan = TimeSpan.Parse(t.break_start.Value.ToString("HH:mm"));
                        if (restStartTimeSpan < workTimeBeginSpan || restStartTimeSpan > workTimeEndSpan) // 不在当天时间点范围内的，加一天
                            timerange.Start = timerange.Start.AddDays(1);

                        restEndTimeSpan = TimeSpan.Parse(t.break_end.Value.ToString("HH:mm"));
                        if (restEndTimeSpan < workTimeBeginSpan || restEndTimeSpan > workTimeEndSpan) // 不在当天时间点范围内的，加一天
                            timerange.End = timerange.End.AddDays(1);

                        list.Add(new TimeRange(timerange.Start, timerange.End));

                        isExistRestTime = true;
                    }
                }
                else // 计算平时加班时长，排除班次时间
                {
                    list.Add(new TimeRange(t.start, t.end));
                }

                // 班次是否有休息时间2，计算时需要排除休息时间2（休息时间1在班次内，不需要重复排除）
                if (t.break_start2.HasValue && t.break_end2.HasValue && t.break_start2 >= t.end)
                {
                    timerange = new TimeRange();
                    timerange.Start = DateTime.Parse($"{t.date.ToString("yyyy-MM-dd")} {t.break_start2.Value.ToString("HH:mm")}");
                    timerange.End = DateTime.Parse($"{t.date.ToString("yyyy-MM-dd")} {t.break_end2.Value.ToString("HH:mm")}");

                    restStartTimeSpan = TimeSpan.Parse(t.break_start2.Value.ToString("HH:mm"));
                    if (restStartTimeSpan < workTimeBeginSpan || restStartTimeSpan > workTimeEndSpan) // 不在当天时间点范围内的，加一天
                        timerange.Start = timerange.Start.AddDays(1);

                    restEndTimeSpan = TimeSpan.Parse(t.break_end2.Value.ToString("HH:mm"));
                    if (restEndTimeSpan < workTimeBeginSpan || restEndTimeSpan > workTimeEndSpan) // 不在当天时间点范围内的，加一天
                        timerange.End = timerange.End.AddDays(1);

                    list.Add(timerange);

                    isExistRestTime = true;
                }
            }
            else // 没有排班，只剔除休息的时间
            {
                if (t.break_start.HasValue && t.break_end.HasValue)
                {
                    timerange = new TimeRange();
                    timerange.Start = DateTime.Parse($"{t.date.ToString("yyyy-MM-dd")} {t.break_start.Value.ToString("HH:mm")}");
                    timerange.End = DateTime.Parse($"{t.date.ToString("yyyy-MM-dd")} {t.break_end.Value.ToString("HH:mm")}");

                    restStartTimeSpan = TimeSpan.Parse(t.break_start.Value.ToString("HH:mm"));
                    if (restStartTimeSpan < workTimeBeginSpan || restStartTimeSpan > workTimeEndSpan) // 不在当天时间点范围内的，加一天
                        timerange.Start = timerange.Start.AddDays(1);

                    restEndTimeSpan = TimeSpan.Parse(t.break_end.Value.ToString("HH:mm"));
                    if (restEndTimeSpan < workTimeBeginSpan || restEndTimeSpan > workTimeEndSpan) // 不在当天时间点范围内的，加一天
                        timerange.End = timerange.End.AddDays(1);

                    list.Add(new TimeRange(timerange.Start, timerange.End));

                    isExistRestTime = true;
                }

                if (t.break_start2.HasValue && t.break_end2.HasValue)
                {
                    timerange = new TimeRange();
                    timerange.Start = DateTime.Parse($"{t.date.ToString("yyyy-MM-dd")} {t.break_start2.Value.ToString("HH:mm")}");
                    timerange.End = DateTime.Parse($"{t.date.ToString("yyyy-MM-dd")} {t.break_end2.Value.ToString("HH:mm")}");

                    restStartTimeSpan = TimeSpan.Parse(t.break_start2.Value.ToString("HH:mm"));
                    if (restStartTimeSpan < workTimeBeginSpan || restStartTimeSpan > workTimeEndSpan) // 不在当天时间点范围内的，加一天
                        timerange.Start = timerange.Start.AddDays(1);

                    restEndTimeSpan = TimeSpan.Parse(t.break_end2.Value.ToString("HH:mm"));
                    if (restEndTimeSpan < workTimeBeginSpan || restEndTimeSpan > workTimeEndSpan) // 不在当天时间点范围内的，加一天
                        timerange.End = timerange.End.AddDays(1);

                    list.Add(timerange);

                    isExistRestTime = true;
                }
            }

            if (IsSameDay(t.date, calcStartTime, 1)) // 排班日期如果跟计算结束时间相差一天，则排除临界点时间
                list.Add(new TimeRange()
                {
                    Start = calcEndTime,
                    End = calcEndTime.AddDays(1),
                });

            if (!isExistRestTime)
                list.Clear();

            return list;
        }

        private bool IsSameDay(DateTime date, DateTime targetDate, int days = 0)
        {
            return date.Year == targetDate.Year &&
                   date.Month == targetDate.Month &&
                   date.Day + days == targetDate.Day;
        }

        private bool HoursDiff(DateTime date, DateTime targetDate, double hours = 4.5, bool isNagavite = false)
        {
            // 计算时间差
            TimeSpan timeDifference = targetDate - date;

            // 获取绝对值，以便无论顺序如何，都能正确比较
            timeDifference = timeDifference.Duration();

            if (isNagavite) // 判断时间差是否大于 指定 小时
                return timeDifference > TimeSpan.FromHours(hours);
            else // 判断时间差是否小于 指定 小时
                return timeDifference <= TimeSpan.FromHours(hours);
        }

        /// <summary>
        /// 计算加班时长
        /// </summary>
        /// <param name="workOnTime"></param>
        /// <param name="workOffTime"></param>
        /// <param name="ignoreStartTimes"></param>
        /// <param name="ignoreEndTimes"></param>
        /// <returns></returns>
        private double CalcEffectiveWorkOverHours(DateTime workOnTime, DateTime workOffTime, List<TimeRange> timeRange)
        {
            // 有效时间（必须为double类型）
            double workOverHours = 0;

            // 是否统计加班时间
            var isCountOvertime = false;

            // 时间步长（必须为double类型）
            double stepMinutes = 30;

            // 修正时间 XX:00、XX:30
            workOnTime = FixWorkTime(workOnTime, true);
            workOffTime = FixWorkTime(workOffTime, false);

            // 遍历每个时间点，从加班开始到结束
            var currentTime = workOnTime;

            while (currentTime < workOffTime)
            {
                isCountOvertime = true;

                // 检查当前时间是否在休息时段内
                for (int i = 0; i < timeRange.Count; i++)
                {
                    // 如果当前时间在休息时段内，则允许累计加班时长
                    if (currentTime >= timeRange[i].Start && currentTime < timeRange[i].End)
                    {
                        isCountOvertime = false;
                        break;
                    }
                }

                currentTime = currentTime.AddMinutes(stepMinutes);

                if (!isCountOvertime)
                    continue;

                if (currentTime <= workOffTime) // 排除currentTime累加后 > workOffTime的情况，比如16:31早退
                    workOverHours += stepMinutes / 60;
            }

            return workOverHours;
        }

        /// <summary>
        /// 自动剔除、合并重叠的时间，并排序
        /// </summary>
        /// <param name="ranges"></param>
        /// <returns></returns>
        private List<TimeRange> MergeOverlappingRanges(List<TimeRange> ranges)
        {
            // Sort ranges by start time
            ranges.Sort((a, b) => a.Start.CompareTo(b.Start));

            List<TimeRange> mergedRanges = new List<TimeRange>();

            foreach (var range in ranges)
            {
                if (mergedRanges.Count == 0 || !mergedRanges[^1].Overlaps(range))
                {
                    mergedRanges.Add(range);
                }
                else
                {
                    mergedRanges[^1] = mergedRanges[^1].Merge(range);
                }
            }

            return mergedRanges.OrderBy(t => t.End).ToList();
        }

        #endregion

        #region QRQC

        public async Task<List<QrqcEmailUser>> GetQrqcEmailUser()
        {
            var list = Query<QrqcEmailUser>("peoples").Data ?? new List<QrqcEmailUser>();

            return list;
        }

        #endregion
    }
}
